/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * All Rights Researved.
 *
 * This library is developed and distributed for free usage,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * You can redistribute and/or modify it under the terms of the
 * Simplified BSD License
 *
 *
 *
 *
 *
 *
 *
 * Manage painting layers
 *
 *
 * User: jinchen
 * Date: Jun 9, 2003
 * Time: 1:51:16 PM
 */
package edu.psu.geovista.app.pcp.plot.gui;

import edu.psu.geovista.app.pcp.Pcp;
import edu.psu.geovista.app.pcp.animation.DataAnimatorService;
import edu.psu.geovista.app.pcp.plot.control.*;
import edu.psu.geovista.app.pcp.plot.control.tools.BasicTool;
import edu.psu.geovista.app.pcp.plot.control.tools.DataSelectTool;
import edu.psu.geovista.app.pcp.plot.layer.*;
import edu.psu.geovista.app.pcp.plot.services.CommonService;
import edu.psu.geovista.app.plot.axis.AxisList;
import edu.psu.geovista.app.plot.axis.XAxisOrder;
import edu.psu.geovista.app.plot.layer.Layer;
import edu.psu.geovista.ui.event.ColorArrayEvent;
import edu.psu.geovista.ui.event.FreezeEvent;
import edu.psu.geovista.ui.event.IndicationEvent;
import edu.psu.geovista.ui.event.SelectionEvent;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.PanelUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.renderable.RenderContext;
import java.util.*;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;




public class BasicPcpUI extends PanelUI
        implements MouseListener, MouseMotionListener {
    public static final boolean DEBUG = true;

    private static final Logger logger=Logger.getLogger(BasicPcpUI.class.getName() );
    static {
        //set up logger
        Level mylevel= Level.INFO;
          ConsoleHandler handler=new ConsoleHandler();
          logger.addHandler(handler);  // 

          logger.setLevel(mylevel);
          handler.setLevel(mylevel );
    }


    private Dimension size;
    Color bgColor = new Color(0, 0, 0);

    boolean doPaint=true;
    RenderContext rc;

    GraphicsConfiguration gc;
    private PcpCanvas plotCanvas;
    protected  Hashtable layers;   //for display plot
    protected Pcp plot;

    protected  DfConfigManager config;
    //tools
    //private Hashtable tools;

    private ToolManager toolsMgr;
    private ActionMgr actionMgr;
    private CommonService service;
    private ToolBar toolBar;
    private MouseObserver mob;
    private PlotSplitControl sc;
    protected DataPainter painter;
    protected JViewport canvasViewport;



    /**
     *
     * @param plot
     */
    public BasicPcpUI(Pcp plot)  {  // 

        this.plot=plot;

        mob=new MouseObserver();
        if (plot.getDataModel() !=null) {
            feedData(plot.getDataModel());

        }
         //if (plot.getDataModel() !=null)

        //System.out.println(" config ->");
        //this.setupCmds() ;//must occur before "config=new ConfigManager(this);"     ??
        this.toolsMgr =new ToolManager(this);//actionController will do setupCmds()

        this.actionMgr =createActionManager(this);//new DfActionManager(this);
        this.toolBar = this.createToolBar(actionMgr);

    }




    public void feedData(edu.psu.geovista.data.model.AppDataModel dataModel) {
       if (dataModel !=null) {
            //AxisLayer need data of Axis, other layer need size info
            this.setupLayer(plot);  //exception thrown here
            //service need dataModel
            this.service=new CommonService(dataModel);

            //Axis Config need axis info
            //if(actionController.getCmds()==null ||actionController.getCmds().size() ==0) {
                // 
                //  
                toolsMgr.disposeTools();
                this.toolsMgr.setupCmds(this);// 
            //}*/


            actionMgr.reset();
            //this.toolBar.setDefault();//once cmds are available, set default mouse motion listener.
            this.addAnimationService() ;//Must appear after "actionController.setupCmds(this);" since it need  DataSelectCmd


           setupConfigMgr();

           if(sc!=null)
           sc.setConfig(config);

            customize(config);// 


            this.plotCanvas.setPreferredSize(this.getPlot().getPlotSize() );

            this.repaintPlotForChangeSize() ;
           setupTooltips();// 
           //following statement are NOT required by others , so can be add at bottom
           mob.setup(this);
           //mob.setYaxisList(this.getPlot().getYaxisList() );

        }
    }

    protected void setupConfigMgr() {
        config=new DfConfigManager(this);  //ConfigManager need axisCmd
    }

    /**
     * this is for subclass to customize the configuration setting
     * @param config
     */
    protected  void customize(ConfigManager config) {
    }

    private void addAnimationService(){
        DataAnimatorService danm=new DataAnimatorService();
        DataSelectTool dst=(DataSelectTool)toolsMgr.getCmd(BasicTool.DataSelectT );
        danm.setResponder(dst);
        service.addService(CommonService.Service_Animation,danm);

    }
    /**
     *  repaint plot only for changing size of the plot
     */
    public void repaintPlotForChangeSize() {
        // 
       //  
        JComponent pcp = (JComponent) this.getPlot();
        pcp.revalidate() ;
        this.getPlotCanvas().revalidate() ;
        this.plotCanvas.repaint() ;

    }

    public void showPopupMenu(JPopupMenu pop,int x, int y){
        pop.show(this.plotCanvas,x,y);
    }

     protected  void setupTooltips() {

    }



    public void paint(Graphics g, JComponent c) {
        super.paint(g,c);
        if (this.getLayers() !=null)
            paintLayers(g,c);
        //g.dispose();
    }

  /* Graphics2D g2d=null;
    int count=0;*/
    /*public Graphics2D getG2d() {
        return g2d;
    }*/


    /*******************************************************************************************************
     *                layers
     *******************************************************************************************************/
    void paintLayers(Graphics g,JComponent c) {
        //System.out.println(" paint layers--.");
//        Thread.dumpStack() ;
        long start,stop; //put is at start of method
        Graphics2D g2d = (Graphics2D) g;
        AffineTransform af = new AffineTransform();
        //DfPcp pcp=(DfPcp)c;
        if (this.getLayers() ==null)return;
        Enumeration layers=getLayers().elements() ;
        ArrayList layerList=Collections.list(layers) ;
        Collections.sort(layerList);

        ImageCacheLayer cachelayer = (ImageCacheLayer) this.getLayer(Layer.CACHE_LAYER );
    


        Iterator iter=layerList.iterator() ;
        while(iter.hasNext()){
             Layer layer=(Layer)iter.next() ;
             if ((layer instanceof RuntimeLayer) ){

                 //String name = layer.getName();
                ( (RuntimeLayer)layer).paintLayer(g2d);
                //logger.fine(layer+" painted");
             }
             else if (layer instanceof ImageLayer
                      ){// 
                 if( cachelayer==null || (cachelayer!=null && ! cachelayer.contain(layer))){
                     //if cache ==null, just paint ImageLayer; else paint image layer only if it is not in cache.
                 //if(  cachelayer.contain(layer)) {
                 BufferedImage image = ((ImageLayer)layer).getImage();
                 g2d.drawRenderedImage(image ,af);
                 //logger.fine("ImageLayer ["+layer+"]  painted");
                 }
             }
            //System.out.println("");
        }

     

    }
    /*******************************************************************************************************
     *                Layer
     *******************************************************************************************************/
    protected  void setupLayer(Pcp plot){
            layers=new Hashtable();

        AxisList yaxisList = plot.getYaxisList();
        XAxisOrder xaxis = plot.getXaxis();
        edu.psu.geovista.data.model.AppDataModel dataModel = plot.getDataModel();
        Dimension plotSize = plot.getPlotSize();

        //painter
        painter = new DataPainter(yaxisList,xaxis,dataModel);// 

        //axis layer
        AxisLayer axisLayer = createAxisLayer(yaxisList, xaxis, plotSize);
        layers.put(axisLayer.getName() ,axisLayer); //Must before instantiate DataLayer

        //data layer
        //ESVisualClassifier vc = this.getPlot().getVc();
        final int rowcount = dataModel.getRowCount();
        Color[] colors=new java.awt.Color[rowcount];
        Color defaultcolor=new Color(200,200,200);
        for (int i=0;i<colors.length ;i++){
            colors[i]=defaultcolor;
        }
        /*if (vc!=null) {
            colors = vc.findDataColors();
        }*/

        DataLayer lineLayer=null;
        try {
            lineLayer = new DataLayer(Layer.BgData_LAYER, dataModel, yaxisList,xaxis ,plotSize,colors);  //all data layer
        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            return;
        }
          layers.put(lineLayer.getName() ,lineLayer); //still need put into layer, simply not being painted individually

            DataLayer showRangeLayer=new DataLayer(Layer.TopData_LAYER, dataModel, yaxisList,xaxis ,plotSize,colors); //show range layer
            showRangeLayer.setVisible(true);//default
            //showRangeLayer.setLevel(lineLayer.getLevel() +1);//just above lineLayer
            layers.put(showRangeLayer.getName() ,showRangeLayer);

           ImageCacheLayer cacheLyr=new ImageCacheLayer(Layer.CACHE_LAYER );//cachelayer contain showrange layer and all data layer
            cacheLyr.setSize(plotSize);
            //todo temp reomved 
            cacheLyr.addToCache(lineLayer);
            cacheLyr.addToCache(showRangeLayer);
            layers.put(cacheLyr.getName() ,cacheLyr);

        //dynamic layer
           DynamicLayer dynamicLayer=new DynamicLayer(Layer.DYNAMIC_LAYER);
            dynamicLayer.setType(Layer.TYPE_DYNAMIC );
            dynamicLayer.setUi(this);
            layers.put(dynamicLayer.getName() ,dynamicLayer);

           //set levels
            dynamicLayer.setLevel(Layer.TOP );
            cacheLyr.setLevel(Layer.MIDDLE );
            lineLayer.setLevel(Layer.MIDDLE -10);
            showRangeLayer.setLevel(lineLayer.getLevel() +1);//just above lineLayer
            axisLayer.setLevel(Layer.MIDDLE +10);


    }

    protected AxisLayer createAxisLayer(AxisList yaxisList, XAxisOrder xaxis, Dimension plotSize) {
        AxisLayer axisLayer=new AxisLayer(Layer.AXIS_LAYER,yaxisList, xaxis ,plotSize ); //axis layer must be first since it decide pcp's size
        axisLayer.setUi(this);
        return axisLayer;

    }

    public DataPainter getPainter() {
        return painter;
    }

    public Hashtable getLayers() {
        return layers;
    }
    public Layer getLayer(String layerId){
        if (layers!=null)
            return (Layer)this.layers.get(layerId);
        else
            return null;
    }
    public Object[] getLayerByType(int type){

        Collection  lys=this.layers.values() ;
        HashSet resultLys=new HashSet();

        Iterator iter=lys.iterator() ;
        while(iter.hasNext() ){
             Layer ly=(Layer)iter.next() ;
             if (ly.getType()==type){
                 resultLys.add(ly);
             }

        }
        return resultLys.toArray();
    }

    /*******************************************************************************************************
     *                UI
     *******************************************************************************************************/
    /**
     * Swing method. Returns a new instance.
     */
    public static ComponentUI createUI(JComponent c) {
        return new BasicPcpUI((Pcp)c);
    }
    /*******************************************************************************************************
     *                install UI
     *******************************************************************************************************/
    public void installUI(JComponent c) {
            //PcpPlot plot=(PcpPlot)c;
            //System.out.println("create plot");
            //plot.addMouseListener(mo);
            //plot.addMouseMotionListener(mo);

            this.installComponent(c);
            //this.pcp =pcp
    }

    public void installComponent(JComponent c) {


        this.plotCanvas =new PcpCanvas(this);
        this.plotCanvas.addMouseListener(mob);
        this.plotCanvas.addMouseMotionListener(mob);
        this.plotCanvas.setPreferredSize(new Dimension(1700,1000));//new Dimension(2700,1000));//this.getPlot().getPlotSize() );




        c.setLayout(new BorderLayout());


        PcpCanvas canvas = this.getPlotCanvas();
        JScrollPane canvasSp = new JScrollPane(canvas);//right is pcpCanvas
        this.canvasViewport =canvasSp.getViewport() ;

        canvas.setContainerScrollPane(canvasSp);

        c.add(canvasSp);

        JPanel selBar = this.createSelBar();
       // c.add(selBar,BorderLayout.WEST );jin: remove the side bar which contain "Configure" button


        c.add(this.toolBar ,BorderLayout.NORTH );


    }
    public void uninstallUI(Pcp pcp) {
    }

     /**************************************************
      *                temp: selBar
      ***************************************************/
      private JPanel createSelBar() {
        JPanel sbar=new JPanel();
        sbar.setLayout(new GridLayout(4,1)); //need set (4,1) so that the configure button will be on the top of the bar
        //JButton axis=new JButton("<html>A<br>X<br> I<br>S</html>");
        JButton showConfigBtn=new JButton("   Configure");//"            Data"
        showConfigBtn.setVerticalAlignment(SwingConstants.CENTER );
        showConfigBtn.setHorizontalAlignment(SwingConstants.CENTER );


                    showConfigBtn.addActionListener(new AbstractAction(){
                            public void actionPerformed(ActionEvent e){
                                if (config!=null){
                                    config.showConfig(ConfigManager.CFG_RANGE);
                                }

                             

                            }
                    });


       //ButtonGroup bgrp=new ButtonGroup();
        makeVerticalButton(showConfigBtn);

        Color color=new Color(244,239,239);
         showConfigBtn.setBackground(color);

        showConfigBtn.setToolTipText("Configure and manipulate plot");//Manipulate data by operating on axis. Such as show range and axes' order") ;
        //data.setToolTipText("Classification and animation on data");
        GridBagConstraints gridBagConstraints;

       //sbar.add(plot);
       sbar.add(showConfigBtn);

       return sbar;
    }


    public  static Font getVerticalFont(AbstractButton btn){
        AffineTransform trans = new AffineTransform();
                        trans.rotate(-Math.PI/2, btn.getWidth(), btn.getHeight()/2);
                        Font font = new Font("", Font.BOLD, 11);
                        font = font.deriveFont(trans);
        return font;
    }

    public static AbstractButton makeVerticalButton(AbstractButton btn){
         btn.setFont(getVerticalFont(btn));
         btn.setVerticalAlignment(SwingConstants.BOTTOM );
         btn.setMargin(new Insets(2,8,2,8));
         btn.setBorderPainted(false);
         btn.setAlignmentX(SwingConstants.TOP );
         return btn;

    }


    /*******************************************************************************************************
     *                Mouse Event
     *******************************************************************************************************/


    public void mousePressed(MouseEvent e) {

    }
     public void mouseDragged(MouseEvent e) {

     }
     public void mouseMoved(MouseEvent e) {
     }
     public void mouseReleased(MouseEvent e) {
     }


     public void mouseEntered(MouseEvent e) {}
     public void mouseExited(MouseEvent e) {}
     public void mouseClicked(MouseEvent e) {}
     /*******************************************************************************************************
      *                Selection Event
      *******************************************************************************************************/
     /**
      * Show selection of ids given by e. Usually, the method is to act on outside event(e.g. Listen to outside beans)
      * @param e
      */
     public void showSelection(SelectionEvent e) {


         DataSelectTool dscmd=(DataSelectTool) this.getToolsMgr().getCmd(BasicTool.DataSelectT );
        // if(ids.length >0){
         if(dscmd!=null)
         dscmd.processSelection(e);
             //dscmd.showDataWithIds(DataGroup.convertArrayToSet(ids),false );
         //}
     }
    public void indicationChanged(IndicationEvent e) {
        int id=e.getIndication() ;
        ToolManager tm=this.getToolsMgr();
        DataSelectTool dscmd=(DataSelectTool) tm.getCmd(BasicTool.DataSelectT );

         if(id>0&&dscmd!=null){
             /**
               
               
               
               
             */
             dscmd.processIndication(id,0,0);

         }
    }
     public void freezeChanged(FreezeEvent e) {
         DataSelectTool dscmd=(DataSelectTool) this.getToolsMgr().getCmd(BasicTool.DataSelectT );
        // if(ids.length >0){
         if(dscmd!=null)
         dscmd.processFreeze(e);
    }
    /*******************************************************************************************************
     *                Color event
     *******************************************************************************************************/
    public void colorArrayChanged(ColorArrayEvent e) {
        Color[] colors=e.getColors() ;
        DataSelectTool dsc=(DataSelectTool) this.toolsMgr.getCmd(BasicTool.DataSelectT );
        if(dsc!=null){
            dsc.setDataLayerColors(colors);
        }
    }
     /*******************************************************************************************************
      *                properties
      *******************************************************************************************************/
    public Pcp getPlot() {
        return plot;
    }

    public PcpCanvas getPlotCanvas() {
        return plotCanvas;
    }

    public JViewport getCanvasViewport() {
        return canvasViewport;
    }

    public ActionMgr getActionMgr() {
        return actionMgr;
    }

    public ToolManager getToolsMgr() {
        return toolsMgr;
    }

    public MouseObserver getMob() {
        return mob;
    }

    public CommonService getService() {
        return service;
    }

   /* public LegendControl2 getLegend() {
       return this.plotCanvas.getLegend();
    }*/

     public void setStandAlone(boolean standAlone) {
             this.actionMgr.setActionEnable("OpenFile",standAlone);

    }

    public Settings getSettings() {

        return this.getPlot().getSettings() ;
    }

  /*  public TooltipManager getTooltipMgr() {
        return tooltipMgr;
    }*/

    public DfConfigManager getConfig() {
        return config;
    }

    /*******************************************************************************************************
     *                properties & utils
     *******************************************************************************************************/

    protected ToolBar createToolBar(ActionMgr actMgr){
        return new ToolBar(actMgr);
    }
    protected ActionMgr createActionManager(BasicPcpUI ui){
        return new DfActionManager(ui);
    }



}


